package edu.hubu.talentmanagementsystem.aop;

import edu.hubu.talentmanagementsystem.common.EncodeParam;
import edu.hubu.talentmanagementsystem.common.SensitiveField;
import edu.hubu.talentmanagementsystem.model.Model;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author moonlan
 * date 2021/3/13 下午3:57
 */
@SuppressWarnings(value = "unused")
@Aspect
@Component
public class EncodeAspect {

    private final Logger logger = LoggerFactory.getLogger(getClass());
    private final BCryptPasswordEncoder encoder;

    public EncodeAspect(BCryptPasswordEncoder encoder) {this.encoder = encoder;}

    @Pointcut("@annotation(edu.hubu.talentmanagementsystem.common.EncodeOperation)")
    public void pc() {}

    @Around("pc()")
    public Object around(ProceedingJoinPoint pjp) {
        Class<?> targetClass = pjp.getTarget().getClass();
        Logger logger = LoggerFactory.getLogger(targetClass);
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        Method method = methodSignature.getMethod();
        try {
            logger.debug("encoding --> {}", method);
            Parameter[] parameters = method.getParameters();
            // 获取方法参数值数组
            Object[] args = pjp.getArgs();
            // 遍历方法参数列表
            for (int i = 0; i < parameters.length; i++) {
                // 如果标注了是EncodeParam的参数
                if (parameters[i].isAnnotationPresent(EncodeParam.class)) {
                    // 获取到该参数的字节码
                    //args[i] = encoder.encode(args[i].toString());
                    Class<?> param = parameters[i].getType();
                    // 如果该参数是实体类
                    if (Model.class.isAssignableFrom(param)) {
                        // 获取到需要加密的field
                        List<Field> fields =
                                Arrays.stream(param.getDeclaredFields()).filter(field -> field.isAnnotationPresent(SensitiveField.class)).collect(Collectors.toList());
                        // 对这些Field逐个加密
                        for (Field field : fields) {
                            field.setAccessible(true);
                            Object o = field.get(args[i]);
                            if (!ObjectUtils.isEmpty(o)) {
                                field.set(args[i], encoder.encode(o.toString()));
                            }
                        }
                    }
                }
            }
            return pjp.proceed();
        } catch (Throwable throwable) {
            logger.error("error --> {}", throwable.getMessage());
            throw new RuntimeException(throwable);
        }
    }

}
